package com.stottlerhenke.simbionic.editor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.tree.DefaultMutableTreeNode;

import com.stottlerhenke.simbionic.api.SB_Exception;
import com.stottlerhenke.simbionic.api.SB_VarType;
import com.stottlerhenke.simbionic.common.classes.SB_ClassDescription;
import com.stottlerhenke.simbionic.common.parser.EParamDir;
import com.stottlerhenke.simbionic.editor.gui.SB_Catalog;
import com.stottlerhenke.simbionic.editor.gui.SB_Polymorphism;
import com.stottlerhenke.simbionic.editor.parser.FuncParamSpec;

public class SB_EditorDataInterface {

  private static SB_Catalog _catalog;
  private static SB_Polymorphism _poly;  // current polymorphism

  private static SB_Catalog getCatalog() { return _catalog; }
  public static void setCatalog(SB_Catalog catalog) { _catalog = catalog; }
  public static SB_Polymorphism getPoly() { return _poly; }
  public static void setPoly(SB_Polymorphism poly) { _poly = poly; }
  
  public SB_EditorDataInterface()
  {
  }
  
  public SB_VarType LookupVariable(String varName)
  {
    SB_Variable var = _catalog.findVariable(_poly, varName);
    if (var != null)
    	return SB_TypeManager.convertOldTypeId(var.getType());
    else
      return SB_VarType.kNoSuchName;
  }

  public SB_VarType LookupFunction(String funcName)
  {
    SB_Function func = _catalog.findFunction(funcName);
    if (func != null)
    {
      if (func instanceof SB_Predicate)
    	  return SB_TypeManager.convertOldTypeId(((SB_Predicate) func).getRetType());
      else
        return SB_VarType.kVoid;
    }
    else
      return SB_VarType.kNoSuchName;
  }

  public ArrayList LookupFunctionParams(String funcName)
  {
    ArrayList vec = new ArrayList();
    DefaultMutableTreeNode treeNode = _catalog.findFuncNode(funcName);
    if (treeNode != null)
    {
      DefaultMutableTreeNode childNode;
      SB_Parameter param;
      FuncParamSpec paramSpec;
      int size = treeNode.getChildCount();
      for (int i = 0; i < size; ++i)
      {
        childNode = (DefaultMutableTreeNode) treeNode.getChildAt(i);
        param = (SB_Parameter) childNode.getUserObject();
        paramSpec = new FuncParamSpec(param.getName(), ConvertDir(param.getDirection()),
        		SB_TypeManager.convertOldTypeId(param.getType()));
        vec.add(paramSpec);
      }
    }
    return vec;
  }

  /**
   * Given an SB_VarType, return a fully qualified java class name.
   * 
   * @param classType SB_VarType of class to get
   * @return a fully qualified java class name if found, null otherwise
   */
  public String LookupClassName(SB_VarType classType)
  {
	  if (_catalog.getClassMap() != null)
		  return _catalog.getClassMap().getJavaClassName(classType.getState());
	  else
		  return null;
  }
 
  /**
   * Given a class alias, return a fully qualified java class name.
   * 
   * @param classAlias alias of class to get
   * @return a fully qualified java class name if found, null otherwise
   */
  public String LookupClassName(String classAlias)
  {
	  if (_catalog.getClassMap() != null)
		  return _catalog.getClassMap().getJavaClassName(classAlias);
	  else
		  return null;
  }
  
  /**
   * Given a Class object, determine is SB_VarType
   * 
   * @param classObject object to find an ID for, may be null in which case SB_VarType.kVoid should be returned
   * @return SB_VarType.kNoSuchName if not found, the proper SB_VarType otherwise
   */
  public SB_VarType LookupClassID(Class classObject)
  {
      if( classObject == null)
          return SB_VarType.kVoid;
      else if (_catalog.getClassMap() == null)
    	  return SB_VarType.kNoSuchName;
      else
    	  return _catalog.getClassMap().getSBVarType(classObject.getName());
  }
  
  /**
   * Given a variable, return its fully qualified java class name
   * 
   * @param varName name of variable
   * @return the fully qualified java class name of the varName, 
   * 	varName if the variable is actually a static class reference
   * 	null otherwise
   */
  public String LookupVariableClassName(String varName)
  {
	  return LookupClassName(LookupVariable(varName));
  }
  
  /**
   * Get the SB_VarType of a class member
   * 
   * @param className fully qualified class name
   * @param memberName class member
   * @return SB_VarType of the member if found, SB_VarType.kNoSuchName otherwise
   */
  public SB_VarType LookupMember(String className, String memberName)
  {
	  SB_Class cls = _catalog.lookupClass(className);
	  if (cls == null)
		  return SB_VarType.kNoSuchName;
	  
	  SB_ClassMember member = cls.lookupMember(memberName);
	  if (member == null)
		  return SB_VarType.kNoSuchName;
	  
	  // set the parent class in-use
	  cls.setInUse(true);
	  
	  // set the member in-use
	  member.setInUse(true);
	  
	  // set the type of the member in-use
	  if (!member.isPrimitiveType()){
		  cls = _catalog.lookupClass(member.getType());
		  if (cls != null)
			  cls.setInUse(true);
	  }

	  try{
		  Class typeClass = member.getTypeClass();
		  if (typeClass == null)
			  return SB_VarType.kVoid;
	      else if( typeClass.equals(Boolean.TYPE) ) 
	    	  return SB_VarType.kBoolean;
	      else if( typeClass.equals(Character.TYPE) ) 
	    	  return SB_VarType.kChar;
	      else if( typeClass.equals(Byte.TYPE) ) 
	    	  return SB_VarType.kByte;
	      else if( typeClass.equals(Short.TYPE) ) 
	    	  return SB_VarType.kShort;
	      else if( typeClass.equals(Integer.TYPE) ) 
	    	  return SB_VarType.kInteger;
	      else if( typeClass.equals(Long.TYPE) ) 
	    	  return SB_VarType.kEntity;
	      else if( typeClass.equals(Float.TYPE) ) 
	    	  return SB_VarType.kFloat;
	      else if( typeClass.equals(Double.TYPE) ) 
	    	  return SB_VarType.kDouble;
	      else
	    	  return LookupClassID(typeClass);
	  }
	  catch (ClassNotFoundException ex){
		  return SB_VarType.kNoSuchName;
	  }
  }
  
  /**
   * Determine whether or not a method exists on a class
   * 
   * @param className fully qualified class name
   * @param methodName name of method
   * @return true if the method name exists the class (parameters not matched)
   */
  public boolean MethodExists(String className, String methodName)
  {
	  SB_Class cls = _catalog.lookupClass(className);
	  if (cls == null)
		  return false;
	  
	  Vector methods = cls.lookupMethods(methodName);
	  if (methods == null || methods.size() <= 0)
		  return false;

	  return true;
  }
  
  /**
   * Find a class description for a class name
   * 
   * @param className fully qualified class name
   * @return matching class description if found, null otherwise
   */
  public SB_ClassDescription LookupClassDescription(String className)
  {
	  SB_Class cls = _catalog.lookupClass(className);
	  if (cls != null)
		  cls.setInUse(true);
		  
	  if (_catalog.getClassMap() != null)
		  return _catalog.getClassMap().getClassDescription(className);
	  else
		  return null;
  }
  
  /**
   * Find a an exact method match based on the arguments
   * 
   * @param className fully qualified classname
   * @param methodName name of method
   * @param args array of SB_ParseNode objects
   * @return a class method description if found, null otherwise
   */
  public com.stottlerhenke.simbionic.common.classes.SB_ClassMethod LookupMethod(String className, 
		  String methodName, ArrayList args) throws SB_Exception
  {
	  SB_ClassDescription classDesc = LookupClassDescription(className);
	  if (classDesc == null)
		  return null;
	  
	  com.stottlerhenke.simbionic.common.classes.SB_ClassMethod methodDesc = 
		  classDesc.getMatchingMethod(className, methodName, args);
	  
	  if (methodDesc != null)
	  {
		  SB_ClassMethod method = _catalog.lookupClass(className).lookupMethod(methodName, methodDesc.overloadIndex);
		  
		  // set the method in-use
		  method.setInUse(true);
		  
		  // set the return type class in-use
		  if (!method.isPrimitiveReturnType()){
			  SB_Class cls = _catalog.lookupClass(method.getReturnType());
			  if (cls != null)
				  cls.setInUse(true);
		  }
		  
		  // set the parameter type classes in-use
		  for (Iterator it2 = method.getParams().iterator(); it2.hasNext();)
		  {
			  SB_ClassMethodParameter param = (SB_ClassMethodParameter) it2.next();
			  if (!param.isPrimitiveType()){
				  SB_Class cls = _catalog.lookupClass(param.getType());
				  if (cls != null)
					  cls.setInUse(true);
			  }
		  }
	  }
	  
	  return methodDesc;
  }
  
  /**
   * @deprecated  replaced by SB_TypeManger.convertOldTypeId(int)
   */
  public static SB_VarType ConvertType(int type)
  {
    switch (type)
    {
      case SB_Variable.kFloat:
        return SB_VarType.kFloat;
      case SB_Variable.kInteger:
        return SB_VarType.kInteger;
      case SB_Variable.kString:
        return SB_VarType.kString;
      case SB_Variable.kEntity:
        return SB_VarType.kEntity;
      case SB_Variable.kBoolean:
        return SB_VarType.kBoolean;
      case SB_Variable.kVector:
        return SB_VarType.kVector;
      case SB_Variable.kData:
        return SB_VarType.kData;
      case SB_Variable.kAny:
        return SB_VarType.kAny;
      case SB_Variable.kArray:
        return SB_VarType.kArray;
      case SB_Variable.kTable:
        return SB_VarType.kArray;
      default:
        return SB_VarType.kNoSuchName;
    }
  }

  public static EParamDir ConvertDir(int dir)
  {
    switch (dir)
    {
      case SB_Parameter.kIn:
        return EParamDir.kIn;
      case SB_Parameter.kOut:
        return EParamDir.kOut;
      case SB_Parameter.kInOut:
        return EParamDir.kInOut;
      default:
        return EParamDir.kNone;
    }
  }
}
